package com.av.acciones.entradasalida;

import org.apache.log4j.Logger;

import com.av.acciones.BaseAccion;
import com.av.db.dataobjects.Adeudo;
import com.av.db.dataobjects.ElementoCatalogo;
import com.av.db.dataobjects.EntradaSalida;
import com.av.db.dataobjects.enums.Catalogo;
import com.av.db.layer.interfaces.AdeudoLayer;
import com.av.db.layer.interfaces.ElementoCatalogoLayer;
import com.av.db.layer.interfaces.EntradaSalidaLayer;
import com.av.exceptions.AvException;
import com.av.rmi.Parametro;
import com.av.rmi.Parametro.Tipo;

/**
 * Accion que agrega una entrada o salida en la base de datos configurada
 * 
 * @author Victor J Morales R
 * 
 */
public class AgregarAccion extends BaseAccion {

	private static Logger log = Logger.getLogger(AgregarAccion.class);
	private EntradaSalidaLayer esl;

	@Override
	public Parametro ejecutar(Parametro parametro) throws AvException {
		log.info("Inicio - ejecutar(Parametro parametro)");

		esl = (EntradaSalidaLayer) getBean(EntradaSalidaLayer.BEAN_NAME);
		EntradaSalida es = null;
		if (parametro.getValor(Tipo.INPUT) instanceof EntradaSalida) {
			es = (EntradaSalida) parametro.getValor(Tipo.INPUT);
			if (!es.isEntrada()) {
				calcularAdeudo(es);
			}
			esl.agregar(es);
			parametro.setValor(Tipo.OUTPUT, es);
		}

		log.info("Fin - ejecutar(Parametro parametro)");

		return parametro;
	}// ejecutar

	/**
	 * Funcion que calcula y registra el adeudo generado por la salida del
	 * automovil
	 * 
	 * @param es
	 * @throws AvException
	 */
	private void calcularAdeudo(EntradaSalida es) throws AvException {
		log.info("Inicio - calcularAdeudo(EntradaSalida es)");

		EntradaSalida ultima = esl.obtenerUltimaEntrada(es.getTarjeta());
		if (ultima != null) {
			ElementoCatalogo costo = obtenerCostoPorHora();
			if (costo != null) {
				double horas = calcularHoras(ultima, es);

				Adeudo a = new Adeudo();
				a.setCosto(horas * costo.getCosto());
				a.setFecha(es.getFecha());
				a.setTarjeta(es.getTarjeta());
				a.setElementoCatalogo(costo);

				registrarAdeudo(a);
			} else {
				log.warn("No se especifico un costo para la cuota del "
						+ "estacionamiento");
			}
		}

		log.info("Fin - calcularAdeudo(EntradaSalida es)");
	}// calcularAdeudo

	/**
	 * Funcion que obtiene el costo por hora para el servicio de estacionamiento
	 * 
	 * @return
	 * @throws AvException
	 */
	private ElementoCatalogo obtenerCostoPorHora() throws AvException {
		log.info("Inicio - obtenerCosto()");

		ElementoCatalogoLayer ecl = (ElementoCatalogoLayer) getBean(ElementoCatalogoLayer.BEAN_NAME);
		ElementoCatalogo[] ec = null;
		if (ecl != null) {
			try {
				ec = ecl.obtener(Catalogo.COSTOHORA);
			} catch (AvException e) {
				log
						.error("Ha ocurrido un error al intentar obtener las cuotas");
				throw e;
			}
		}

		log.info("Fin - obtenerCosto()");

		return ec != null && ec.length > 0 ? ec[0] : null;
	}// obtenerCosto

	/**
	 * Funcion que calcula las horas registradas para el vehiculo a partir de su
	 * hora de salida
	 * 
	 * @param entrada
	 * @param salida
	 * @return
	 * @throws AvException
	 */
	private double calcularHoras(EntradaSalida e, EntradaSalida s)
			throws AvException {
		log.info("Inicio - calcularHoras(EntradaSalida e, EntradaSalida s)");

		// Validacion de entrada
		if (e == null || e.getFecha() == null) {
			log
					.error("No se puede calcular las horas a partir de una entrada nula");
			throw new AvException(
					"No se puede calcular las horas a partir de una entrada nula");
		}

		if (!e.isEntrada()) {
			log.error("El objeto usado como entrada no es una entrada");
			throw new AvException(
					"El objeto usado como entrada no es una entrada");
		}

		// Validacion de salida
		if (s == null || s.getFecha() == null) {
			log
					.error("No se puede calcular las horas a partir de una entrada nula");
			throw new AvException(
					"No se puede calcular las horas a partir de una entrada nula");
		}

		if (s.isEntrada()) {
			log.error("El objeto usado como salida no es una salida");
			throw new AvException(
					"El objeto usado como salida no es una salida");
		}

		if (s.getFecha().before(e.getFecha())) {
			log.error("La fecha de salida es anterior a la de entrada");
			throw new AvException(
					"La fecha de salida es anterior a la de entrada");
		}

		long horas = s.getFecha().getTime() - e.getFecha().getTime();
		log.debug("Horas caluladas : " + horas);

		log.info("Fin - calcularHoras(EntradaSalida e, EntradaSalida s)");

		return horas / (1000 * 60 * 60);
	}// calcularHoras

	/**
	 * Funcion que registra el adeudo enviado como parametro
	 * 
	 * @param a
	 * @throws AvException
	 */
	private void registrarAdeudo(Adeudo a) throws AvException {
		log.info("Inicio - registrarAdeudo(Adeudo a)");

		AdeudoLayer al = (AdeudoLayer) getBean(AdeudoLayer.BEAN_NAME);
		try {
			al.agregar(a);
		} catch (AvException e) {
			log
					.error("Problemas al registrar el adeudo generado por la salida "
							+ "del vehiculo");
			throw e;
		}

		log.info("Fin - registrarAdeudo(Adeudo a)");
	}// registrarAdeudo
}// AgregarAccion